package com.tuanzi.loan.web.configuration;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.jumai.server.qiniu.QiniuUploadUtils;
import com.qiniu.common.Zone;
import com.qiniu.storage.Configuration;
import com.tuanzi.loan.business.constants.DictionaryCode;
import com.tuanzi.loan.business.entity.common.CommProductChannel;
import com.tuanzi.loan.business.entity.common.CommProductId;
import com.tuanzi.loan.business.entity.family.ProductInfo;
import com.tuanzi.loan.business.entity.system.Dictionary;
import com.tuanzi.loan.business.service.common.CommProductChannelService;
import com.tuanzi.loan.business.service.common.CommProductIdService;
import com.tuanzi.loan.business.service.family.ProductInfoService;
import com.tuanzi.loan.business.service.system.DictionaryService;
import com.tuanzi.loan.core.exception.SystemException;
import com.tuanzi.loan.core.util.DesensitizationUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.primefaces.context.RequestContext;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.UploadedFile;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author Ray 2017/09/21
 */
@Slf4j
public abstract class JSFBean implements Serializable {

	private static final long serialVersionUID = 1647434666521890905L;
	private static final String REDIRECT_SUFFIX = "faces-redirect=true";

	@Autowired
	protected DictionaryService dictionaryService;
	@Autowired
	private CommProductIdService commProductIdService;
	@Autowired
	private CommProductChannelService commProductChannelService;
	@Autowired
	private ProductInfoService productInfoService;

	// 传参
	private Object param;

	@Getter
	private Map<String /* Dictonary Code */, List<Dictionary>> dictionaries;
	private List<CommProductId> commProductIds;
	private List<CommProductId> appCommProductIds;
	private List<CommProductChannel> commProductChannels;
	private List<ProductInfo> productInfos;
	private OperationMode mode = OperationMode.NORMAL;
	private OperationStatus status = OperationStatus.VIEW;

	public List<CommProductId> getCommProductIds() {
		if (CollectionUtils.isEmpty(commProductIds)) {
			commProductIds = commProductIdService.findAll();
		}
		return commProductIds;
	}

	public List<CommProductId> getAppCommProductIds(){
		if(CollectionUtils.isEmpty(appCommProductIds)){
			appCommProductIds = commProductIdService.findAllAppProducts();
		}
		return appCommProductIds;
	}

	public List<CommProductChannel> getCommProductChannels() {
 		if (CollectionUtils.isEmpty(commProductChannels)) {
			commProductChannels = commProductChannelService.findAll();
		}
		return commProductChannels;
	}

	public List<ProductInfo> getProductInfos(){
		if (CollectionUtils.isEmpty(productInfos)) {
			productInfos = productInfoService.findAll();
		}
		return productInfos;
	}

	@PostConstruct
	private void init() {
		dictionaries = dictionaryService.batchFindByCodes(getDictionaryCodes());
		if (getExternalContext().getFlash().isEmpty() == false) {
			param = getExternalContext().getFlash().get("param");
		}
	}

	protected Object getParam() {
		return param;
	}

	protected final String getParamFromRequest(String key) {
		return getFacesContext().getExternalContext().getRequestParameterMap().get(key);
	}

	protected List<String> getDictionaryCodes() {
		return Lists.newArrayList(DictionaryCode.CHANNEL_GROUP);
	}

	public String translate(String dictionaryCode, Object value) {
		if (value == null) {
			return StringUtils.EMPTY;
		}
		List<Dictionary> dic = dictionaries.get(dictionaryCode);

		if (CollectionUtils.isEmpty(dic)) {
			return value.toString();
		}

		return dic.stream().filter(x -> StringUtils.equals(x.getValue1(), value.toString())).findFirst().map(Dictionary::getName).orElse(value.toString());
	}

	public String translateProductChannel(Integer channelCode) {
		if (channelCode == null) {
			return StringUtils.EMPTY;
		}

		return getCommProductChannels().parallelStream().filter(x -> x.getChannelCode().equals(channelCode)).map(CommProductChannel::getChannelName).findFirst()
				.orElse(channelCode.toString());
	}

	public String translateChannelGroup(Integer channelCode) {
		if (channelCode == null) {
			return StringUtils.EMPTY;
		}

		CommProductChannel commProductChannel = getCommProductChannels().parallelStream().filter(x -> x.getChannelCode().equals(channelCode)).findFirst().orElse(null);

		if (commProductChannel != null && commProductChannel.getChannelGroup() != null) {
			return translate(DictionaryCode.CHANNEL_GROUP, commProductChannel.getChannelGroup());
		}

		return null;
	}

	public String desensitize(String str) {
		return DesensitizationUtils.desensitize(str);
	}

	public String translateProduct(Integer productId) {
		if (productId == null) {
			return StringUtils.EMPTY;
		}

		return getCommProductIds().parallelStream().filter(x -> x.getProductId().equals(productId)).map(CommProductId::getName).findFirst().orElse(productId.toString());
	}

	public String translateProductById(Long id) {
		if (id == null) {
			return StringUtils.EMPTY;
		}

		return getCommProductIds().parallelStream().filter(x -> Objects.equals(x.getId(), id)).map(CommProductId::getName).findFirst().orElse(id.toString());
	}

	public String translateProductInfoName(Long productId) {
		if (productId == null) {
			return StringUtils.EMPTY;
		}

		if (CollectionUtils.isEmpty(productInfos)) {
			productInfos = productInfoService.findAll();
		}

		return getProductInfos().parallelStream().filter(x -> x.getId().equals(productId)).map(ProductInfo::getName).findFirst().orElse(productId.toString());
	}

	protected final void putParamToFlash(String key, Object value) {
		getFacesContext().getExternalContext().getFlash().put(key, value);
	}

	@SuppressWarnings("unchecked")
	protected final <A> A getParamFromFlash(String key) {
		return (A) getFacesContext().getExternalContext().getFlash().get(key);
	}

	protected FacesMessage addInfoMessage(String detail) {
		return addInfoMessage(StringUtils.EMPTY, detail);
	}

	protected FacesMessage addInfoMessage(String summary, String detail) {
		return addMessage(FacesMessage.SEVERITY_INFO, summary, detail);
	}

	protected FacesMessage addWarnMessage(String detail) {
		return addWarnMessage(StringUtils.EMPTY, detail);
	}

	protected FacesMessage addWarnMessage(String summary, String detail) {
		return addMessage(FacesMessage.SEVERITY_WARN, summary, detail);
	}

	protected FacesMessage addErrorMessage(String detail) {
		return addErrorMessage(StringUtils.EMPTY, detail);
	}

	protected FacesMessage addErrorMessage(String summary, String detail) {
		return addMessage(FacesMessage.SEVERITY_ERROR, summary, detail);
	}

	protected FacesMessage addSuccessfullyMessage() {
		return addInfoMessage("操作成功");
	}

	protected FacesMessage addFiledMessage(String reason) {
		return addErrorMessage("操作失败 : " + reason);
	}

	private FacesMessage addMessage(FacesMessage.Severity severity, String summary, String detail) {
		return addMessage(new FacesMessage(severity, summary, detail));
	}

	private FacesMessage addMessage(FacesMessage facesMessage) {
		getFacesContext().addMessage(null, facesMessage);
		return facesMessage;
	}

	protected String uploadImg(UploadedFile uploadedFile) {
		try (InputStream inputStream = uploadedFile.getInputstream()) {
			String a = QiniuUploadUtils.updateImg("loan-management", inputStream, UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(uploadedFile.getFileName()),
					new Configuration(Zone.zone0()));
			log.info("upload result path = {}", a);
			return a;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new SystemException(e);
		}
	}

	protected final FacesContext getFacesContext() {
		FacesContext ctx = FacesContext.getCurrentInstance();
		while (ctx == null) { // 多线程环境中有获取不到的可能性
			ctx = FacesContext.getCurrentInstance();
		}
		return ctx;
	}

	protected final ExternalContext getExternalContext() {
		return getFacesContext().getExternalContext();
	}

	protected String redirect(String url) {
		Preconditions.checkArgument(StringUtils.isNotBlank(url));

		if (url.indexOf("?") != -1) {
			return url + "&" + REDIRECT_SUFFIX;
		} else {
			return url + "?" + REDIRECT_SUFFIX;
		}
	}

	protected String redirect(String url, Object param) {
		if (param != null)
			getExternalContext().getFlash().put("param", param);
		return redirect(url);
	}

	protected void forceRedirect(String url) {
		try {
			String contextPath = getExternalContext().getRequestContextPath();
			getExternalContext().redirect(contextPath + url);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

	protected String getRequestContextPath() {
		return getFacesContext().getExternalContext().getRequestContextPath();
	}

	protected final RequestContext getRequestContext() {
		RequestContext rc = RequestContext.getCurrentInstance();
		while (rc == null) { // 多线程环境中有获取不到的可能性
			rc = RequestContext.getCurrentInstance();
		}
		return rc;
	}

	protected void update(String... path) {
		Arrays.asList(path).parallelStream().forEach(x -> update(x));
	}

	protected void update(List<String> paths) {
		paths.parallelStream().forEach(x -> update(x));
	}

	protected void update(String path) {
		RequestContext requestContext = getRequestContext();
		requestContext.update(path);
	}

	protected void execute(String script) {
		RequestContext requestContext = getRequestContext();
		requestContext.execute(script);
	}

	@SuppressWarnings("unchecked")
	protected <T extends Serializable> T deepCopy(T t) {
		Gson gson = new Gson();
		return (T) gson.fromJson(gson.toJson(t), t.getClass());
	}

	protected StreamedContent download(String content, String contentType, String fileName) {
		InputStream inputStream = new ReaderInputStream(new StringReader(content), StandardCharsets.UTF_8.displayName());
		return new DefaultStreamedContent(inputStream, contentType, fileName);
	}

	public void changeStatusToView() {
		this.status = OperationStatus.VIEW;
	}

	public void changeStatusToEdit() {
		this.status = OperationStatus.EDIT;
	}

	public void changeStatusToAdd() {
		this.status = OperationStatus.ADD;
	}

	public void changeModeToNormal() {
		this.mode = OperationMode.NORMAL;
	}

	public void changeModeToBatch() {
		this.mode = OperationMode.BATCH;
	}

	public boolean isView() {
		return status == OperationStatus.VIEW;
	}

	public boolean isEdit() {
		return status == OperationStatus.EDIT;
	}

	public boolean isAdd() {
		return status == OperationStatus.ADD;
	}

	public boolean isBatch() {
		return mode == OperationMode.BATCH;
	}

	public boolean isNormal() {
		return mode == OperationMode.NORMAL;
	}

	public boolean isNotView() {
		return status != OperationStatus.VIEW;
	}

	public boolean isNotEdit() {
		return status != OperationStatus.EDIT;
	}

	public boolean isNotAdd() {
		return status != OperationStatus.ADD;
	}

	public boolean isNotBatch() {
		return mode != OperationMode.BATCH;
	}

	public boolean isNotNormal() {
		return mode != OperationMode.NORMAL;
	}

	/*
	 * 判断是否为数值导出表单为数值形式
	 */
	public void postProcessXLS(Object document) {
		HSSFWorkbook wb = (HSSFWorkbook) document;
		HSSFSheet sheet = wb.getSheetAt(0);
		for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
			for (int j = 0; j < sheet.getRow(i).getPhysicalNumberOfCells(); j++) {
				String stringCellValue = sheet.getRow(i).getCell(j).getStringCellValue();
				if (NumberUtils.isNumber(stringCellValue)) {
					sheet.getRow(i).getCell(j).setCellValue(Double.valueOf(stringCellValue));
					sheet.getRow(i).getCell(j).setCellType(HSSFCell.CELL_TYPE_NUMERIC);
				}
			}
		}
	}
}
